#include "GStreamer.h"
#include "GstVideoReceiver.h"

#include <QDebug>
#include <gst/gst.h>


Q_LOGGING_CATEGORY(GStreamerLog, "GStreamerLog")
Q_LOGGING_CATEGORY(GStreamerAPILog, "GStreamerAPILog")

G_BEGIN_DECLS
    GST_PLUGIN_STATIC_DECLARE(qmlgl);
    GST_PLUGIN_STATIC_DECLARE(mplay);
G_END_DECLS

static void qt_gst_log(GstDebugCategory * category,
                       GstDebugLevel      level,
                       const gchar      * file,
                       const gchar      * function,
                       gint               line,
                       GObject          * object,
                       GstDebugMessage  * message,
                       gpointer           data)
{
    Q_UNUSED(data);

    if (level > gst_debug_category_get_threshold(category)) {
        return;
    }

    QMessageLogger log(file, line, function);

    char* object_info = gst_info_strdup_printf("%" GST_PTR_FORMAT, static_cast<void*>(object));

    switch (level) {
    default:
    case GST_LEVEL_ERROR:
        log.critical(GStreamerAPILog, "%s %s", object_info, gst_debug_message_get(message));
        break;
    case GST_LEVEL_WARNING:
        log.warning(GStreamerAPILog, "%s %s", object_info, gst_debug_message_get(message));
        break;
    case GST_LEVEL_FIXME:
    case GST_LEVEL_INFO:
        log.info(GStreamerAPILog, "%s %s", object_info, gst_debug_message_get(message));
        break;
    case GST_LEVEL_DEBUG:
    case GST_LEVEL_LOG:
    case GST_LEVEL_TRACE:
    case GST_LEVEL_MEMDUMP:
        log.debug(GStreamerAPILog, "%s %s", object_info, gst_debug_message_get(message));
        break;
    }

    g_free(object_info);
    object_info = nullptr;
}

void GStreamer::initialize(int argc, char *argv[])
{
    QString currentDir = QCoreApplication::applicationDirPath() + "/gstreamer-plugins";
    qputenv("GST_PLUGIN_PATH", currentDir.toStdString().c_str());

    //-- 如果未通过环境配置gstreamer调试，则使用内部QT日志记录
    if (!qEnvironmentVariableIsEmpty("GST_DEBUG")) {
        gst_debug_set_default_threshold(static_cast<GstDebugLevel>(GST_LEVEL_INFO));
        gst_debug_remove_log_function(gst_debug_log_default);
        gst_debug_add_log_function(qt_gst_log, nullptr, nullptr);
    }

    GError* error = nullptr;
    if (!gst_init_check(&argc, &argv, &error)) {
        qCCritical(GStreamerLog) << "gst_init_check() failed: " << error->message;
        g_error_free(error);
    }

    /* 在加载QML文件之前，必须加载插件QML项GstGLVideoItem
      * FIXME：将QQmlExtensionPlugin添加到qmlglsink中，以将GstGLVideoItem注册为QML引擎，然后删除它
      */
    GstElement *sink = gst_element_factory_make("qmlglsink", nullptr);

    if (sink == nullptr) {
        GST_PLUGIN_STATIC_REGISTER(qmlgl);
        sink = gst_element_factory_make("qmlglsink", nullptr);
    }

    if (sink != nullptr) {
        gst_object_unref(sink);
        sink = nullptr;
    } else {
        qCCritical(GStreamerLog) << "unable to find qmlglsink - you need to build it yourself and add to GST_PLUGIN_PATH";
    }

    GST_PLUGIN_STATIC_REGISTER(mplay);
}

void *GStreamer::createVideoSink(QObject *parent, QQuickItem *widget)
{
    Q_UNUSED(parent)

    GstElement* sink;

    if ((sink = gst_element_factory_make("mplayvideosinkbin", nullptr)) != nullptr) {
        g_object_set(sink, "widget", widget, NULL);
    } else {
        qCCritical(GStreamerLog) << __FUNCTION__ "element mplayvideosinkbin failed";
    }

    return sink;
}

void GStreamer::releaseVideoSink(void *sink)
{
    if (sink != nullptr) {
        gst_object_unref(GST_ELEMENT(sink));
    }
}

IVideoReceiver *GStreamer::createVideoReceiver(QObject *parent)
{
    Q_UNUSED(parent)
    return new GstVideoReceiver(nullptr);
}
